Introduction

In this report, we will be doing a Principal Component analysis based on Spotify data on popular songs.

A Principal Component Analysis is a technique to analyze large datasets containing multiple dimensions/features. The purpose is to enable visualization of multidimensional data.

PCA identifies the main axes of variance within a data set and allows for easy data exploration to understand the key variables in the data and spot outliers.

The goal is to determine which variables are related or not related to each other.

library(tidyverse)
library(janitor)

Importing the data

We are using a dataset of popular songs on Spotify. In the previous report on pre-processing, descriptive and bivariate statistics, we have described this dataset and made several transformations.

Link to original dataset

setwd("~/Documents/class/stats-final-project/")
Warning: The working directory was changed to /Users/sadeline/Documents/class/stats-final-project inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
dd <- read.csv("cleaneddata.csv") %>% mutate(
  time_signature = as.character(time_signature)
)

Performing the Principal Component Analysis

We use the prcomp() function.

This returns 3 things:

  1. x => contains the principal components (PCs) for drawing a graph. We will be using the first two columns in x to draw a 2D plot that uses the first 2 PCs. The number of PCs is determined by the number of variables used.

  2. sdev

  3. rotation

Below is the code to run the prcomp function, and a basic plot of principal component 1 and 2.

# determine which ones are numerical variables
numerical <- which(sapply(dd,is.numeric))
# print below to see if the numerical variables are correctly detected
# numerical

# saving the numerical observations to "dcon"
dcon <- dd[,numerical]
# print below to see if variables detected are indeed numerical
# sapply(dcon,class)

# Now we do a PRINCIPAL COMPONENT ANALYSIS on the numerical variables
pca <- prcomp(dcon, scale=TRUE, center = TRUE) # centering and scaling true
pc1 <- pca

plot(pca$x[,1], pca$x[,2])

Scree plot

With the principal component analysis, we can also compute the Scree plot, which displays the variance accounted for by the components.

pca.var <- pca$sdev^2
pca.var.per <- round(pca.var/sum(pca.var)*100, 1)
pca.var.per
 [1] 26.9 14.2 11.8 10.6  8.3  7.5  6.9  6.0  3.6  3.0  1.3
barplot(pca.var.per, main="Scree Plot", xlab="Principal Component", ylab="Percent Variation")


#Cummulated Inertia in subspaces, from first principal component to the 11th dimension subspace
barplot(100*cumsum(pc1$sdev[1:dim(dcon)[2]]^2)/dim(dcon)[2])

percInerAccum<-100*cumsum(pc1$sdev[1:dim(dcon)[2]]^2)/dim(dcon)[2]
percInerAccum
 [1]  26.91234  41.15655  52.92813  63.56836  71.84598  79.34463  86.22294  92.19206  95.78665  98.74806
[11] 100.00000

From this we see that Principal component 1 accounts for only 26.9% of the variation. And in order to account for 80% of the variation, we need to include PC 1-7.

Next we will store the eigenvalues, eigenvectors, projections and include PC 1-7.

# SELECTION OF THE SINGIFICANT DIMENSIONS (keep 80% of total inertia)
nd = 7
pc1$rotation
# STORAGE OF THE EIGENVALUES, EIGENVECTORS AND PROJECTIONS IN THE nd DIMENSIONS
Psi = pc1$x[,1:nd]
Psi
# STORAGE OF LABELS FOR INDIVIDUALS AND VARIABLES
iden = row.names(dcon)
etiq = names(dcon) # getting names of numerical variables
ze = rep(0,length(etiq)) # WE WILL NEED THIS VECTOR AFTERWARDS FOR THE GRAPHICS

Plotting the individuals on PC1 and PC2 axes

# PLOT OF INDIVIDUALS

#select your axis
#eje1<-2
eje1<-1
#eje2<-3
eje2<-2

plot(Psi[,eje1],Psi[,eje2])
text(Psi[,eje1],Psi[,eje2],labels=iden, cex=0.5)
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")

Plotting projection of variables, PC1 and PC2

We will create a loadings plot to see which variables most influence PC1 and PC2.

#Projection of variables

Phi = cor(dcon,Psi)
Phi
                         PC1         PC2         PC3          PC4           PC5         PC6          PC7
popularity       -0.25153061  0.08198524  0.04787668 -0.776358728  0.1270312037 -0.16056296  0.359065114
duration_ms      -0.09138802  0.58531504 -0.18968378  0.075178804  0.4446524295 -0.58574289 -0.116380945
danceability     -0.38602544 -0.58390036 -0.35836660  0.268458136  0.2760524613 -0.24189166  0.070197679
energy           -0.85174751  0.29428633  0.09495404  0.194477832  0.0008212764  0.18993894 -0.006828433
loudness         -0.87255545  0.07126792  0.01756260 -0.062729926  0.0144974134  0.18207412  0.014498890
speechiness      -0.12714215 -0.21242983  0.60931479  0.424680399 -0.0511402462 -0.26382962  0.542555707
acousticness      0.70702764 -0.44238120  0.12823351 -0.207996111 -0.0805660637 -0.21920809 -0.120653957
instrumentalness  0.53137677  0.34622971 -0.20001793  0.469699797 -0.0473689265  0.04368884  0.059121230
liveness         -0.16940196  0.05524380  0.78234881 -0.002361446  0.0622204014 -0.15271351 -0.479746966
valence          -0.54122196 -0.62398564 -0.18906447  0.058291474  0.0089392328 -0.09771015 -0.252593624
tempo            -0.36450385  0.17835956 -0.20813396 -0.024004157 -0.7777975541 -0.41928583 -0.050769168
X<-Phi[,eje1]
Y<-Phi[,eje2]

#zooms
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(min(X,0),max(X,0)), ylim=c(-1,1))
axis(side=1, pos= 0, labels = F)
axis(side=3, pos= 0, labels = F)
axis(side=2, pos= 0, labels = F)
axis(side=4, pos= 0, labels = F)
arrows(ze, ze, X, Y, length = 0.07,col="blue")
text(X,Y,labels=etiq,col="darkblue", cex=0.7)

Observations:

Based on the Loadings plot, we see that the variables that most influence PC1 are instrumentalness, energy, loudness, and acousticness.

The variables that most influence PC2 are duration.

From this plot we can also see that these variables may be positively correlated: - Loudness and energy - Valence and danceability - Instrumentalness and acousticness

These variables may be negatively correlated: - Loudness and instrumentalness - Loudness and acousticness - Energy and acousticness - Valence and instrumentalness - Tempo and acousticness

These variables are orthogonal/may not be very related to each other: - Duration vs loudness - Duration vs instrumentalness - Speechiness vs acousticness

We can also try plotting the PC1 against PC3 to see the variables that are not so clearly visible here.

Plotting projection of variables, PC1 and PC3

Since PC1 and PC2 only accounts for 41% of the variation, we should also plot projection of variables in PC1 and PC3.

#Projection of variables

Phi = cor(dcon,Psi)
Phi
                         PC1         PC2         PC3          PC4           PC5         PC6          PC7
popularity       -0.25153061  0.08198524  0.04787668 -0.776358728  0.1270312037 -0.16056296  0.359065114
duration_ms      -0.09138802  0.58531504 -0.18968378  0.075178804  0.4446524295 -0.58574289 -0.116380945
danceability     -0.38602544 -0.58390036 -0.35836660  0.268458136  0.2760524613 -0.24189166  0.070197679
energy           -0.85174751  0.29428633  0.09495404  0.194477832  0.0008212764  0.18993894 -0.006828433
loudness         -0.87255545  0.07126792  0.01756260 -0.062729926  0.0144974134  0.18207412  0.014498890
speechiness      -0.12714215 -0.21242983  0.60931479  0.424680399 -0.0511402462 -0.26382962  0.542555707
acousticness      0.70702764 -0.44238120  0.12823351 -0.207996111 -0.0805660637 -0.21920809 -0.120653957
instrumentalness  0.53137677  0.34622971 -0.20001793  0.469699797 -0.0473689265  0.04368884  0.059121230
liveness         -0.16940196  0.05524380  0.78234881 -0.002361446  0.0622204014 -0.15271351 -0.479746966
valence          -0.54122196 -0.62398564 -0.18906447  0.058291474  0.0089392328 -0.09771015 -0.252593624
tempo            -0.36450385  0.17835956 -0.20813396 -0.024004157 -0.7777975541 -0.41928583 -0.050769168
eje3 <- 3

X<-Phi[,eje1]
Y<-Phi[,eje3]

#zooms
plot(Psi[,eje1],Psi[,eje3],type="n",xlim=c(min(X,0),max(X,0)), ylim=c(-1,1))
axis(side=1, pos= 0, labels = F)
axis(side=3, pos= 0, labels = F)
axis(side=2, pos= 0, labels = F)
axis(side=4, pos= 0, labels = F)
arrows(ze, ze, X, Y, length = 0.07,col="blue")
text(X,Y,labels=etiq,col="darkblue", cex=0.7)

Based on the above Loadings plot, we see that the variables that most influence PC3 are popularity, liveness and duration.

It also seems like Popularity and liveness are closely related, while it may be negatively correlated with duration.

We can also see that speechiness is closer to danceability.

Finding Centroids

We can also find the centroids of modalities in categorical variables, using the code below.

That looks a bit crowded, so let’s try looking at each categorical variables’ modalities one by one.

Explicit

Explicit songs/songs that contain swear words are more likely to be energetic. This could be Latino songs or rap songs.


X<-Phi[,eje1]
Y<-Phi[,eje2]

#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,1), ylim=c(-1,1))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)

#nominal qualitative variables

dcat<-c(3)
colors<-rainbow(length(dcat))

c<-1
for(k in dcat){
  seguentColor<-colors[c]
fdic1 = tapply(Psi[,eje1],dd[,k],mean)
fdic2 = tapply(Psi[,eje2],dd[,k],mean) 

text(fdic1,fdic2,labels=levels(factor(dd[,k])),col=seguentColor, cex=0.6)
c<-c+1
}
legend("bottomleft",names(dd)[dcat],pch=1,col=colors, cex=0.6)

NA
NA

Key

When we plot the modality of keys, it’s interesting because you can see that some keys are associated with energetic music, such as the “black” keys F#, G# and C#.

Some other keys such as the more popular C, D and G are associated with more acoustic music.

E and A are more danceable, speechy songs (probably, children’s music)?

It fits this description that key signatures have music characteristics: “Noisy shouts of joy, laughing pleasure and not yet complete, full delight lies in E Major.”

#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,1), ylim=c(-1,1))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)

#nominal qualitative variables

dcat<-c(6)
colors<-rainbow(length(dcat))

c<-1
for(k in dcat){
  seguentColor<-colors[c]
fdic1 = tapply(Psi[,eje1],dd[,k],mean)
fdic2 = tapply(Psi[,eje2],dd[,k],mean) 

text(fdic1,fdic2,labels=levels(factor(dd[,k])),col=seguentColor, cex=0.6)
c<-c+1
}
legend("bottomleft",names(dd)[dcat],pch=1,col=colors, cex=0.6)

NA
NA

Mode

#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,1), ylim=c(-1,1))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)

#nominal qualitative variables

dcat<-c(8)
colors<-rainbow(length(dcat))

c<-1
for(k in dcat){
  seguentColor<-colors[c]
fdic1 = tapply(Psi[,eje1],dd[,k],mean)
fdic2 = tapply(Psi[,eje2],dd[,k],mean) 

text(fdic1,fdic2,labels=levels(factor(dd[,k])),col=seguentColor, cex=0.6)
c<-c+1
}
legend("bottomleft",names(dd)[dcat],pch=1,col=colors, cex=0.6)

NA
NA

Multiple artists

This plot shows that songs that have multiple artists are more likely to be acoustic.

#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,3), ylim=c(-2,2))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)

#nominal qualitative variables

dcat<-c(17)
colors<-rainbow(length(dcat))

c<-1
for(k in dcat){
  seguentColor<-colors[c]
fdic1 = tapply(Psi[,eje1],dd[,k],mean)
fdic2 = tapply(Psi[,eje2],dd[,k],mean) 

text(fdic1,fdic2,labels=levels(factor(dd[,k])),col=seguentColor, cex=0.6)
c<-c+1
}
legend("bottomleft",names(dd)[dcat],pch=1,col=colors, cex=0.6)

NA
NA

Tempo category (an ordinal variable)

Slower songs are on the right side, and faster songs are on the left.


#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,3), ylim=c(-1,1))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)


#add ordinal qualitative variables. Ensure ordering is the correct

dordi<-c(18)


#reorder modalities: when required
dd[,dordi[1]] <- factor(dd[,dordi[1]], ordered=TRUE,  levels= c('Larghissimo','Grave','Lento/Largo','Larghetto','Adagio','Andante','Moderato','Allegro','Vivace','Presto','Prestissimo'))
levels(dd[,dordi[1]])
 [1] "Larghissimo" "Grave"       "Lento/Largo" "Larghetto"   "Adagio"      "Andante"     "Moderato"   
 [8] "Allegro"     "Vivace"      "Presto"      "Prestissimo"
c<-1
col<-length(dcat)+1
for(k in dordi){
  seguentColor<-colors[col]
  fdic1 = tapply(Psi[,eje1],dd[,k],mean)
  fdic2 = tapply(Psi[,eje2],dd[,k],mean) 
  
  #points(fdic1,fdic2,pch=16,col=seguentColor, labels=levels(dd[,k]))
  #connect modalities of qualitative variables
  lines(fdic1,fdic2,col="#000000")
  text(fdic1,fdic2,labels=levels(dd[,k]),col=seguentColor, cex=0.6)
  c<-c+1
  col<-col+1
}
legend("topleft",names(dd)[dordi],pch=19,col=colors[col:col+length(dordi)-1], cex=0.6)

NA
NA

All categorical variables


#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,2), ylim=c(-1.5,1))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)

#nominal qualitative variables

dcat<-c(3,6,8,17)
colors<-rainbow(length(dcat))

c<-1
for(k in dcat){
  seguentColor<-colors[c]
fdic1 = tapply(Psi[,eje1],dd[,k],mean)
fdic2 = tapply(Psi[,eje2],dd[,k],mean) 

text(fdic1,fdic2,labels=levels(factor(dd[,k])),col=seguentColor, cex=0.6)
c<-c+1
}
legend("bottomleft",names(dd)[dcat],pch=1,col=colors, cex=0.6)

All categorical variables (including tempo) except genre


#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,3), ylim=c(-2,2))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)

#nominal qualitative variables

dcat<-c(3,6,8,17)
colors<-rainbow(length(dcat))

c<-1
for(k in dcat){
  seguentColor<-colors[c]
fdic1 = tapply(Psi[,eje1],dd[,k],mean)
fdic2 = tapply(Psi[,eje2],dd[,k],mean) 

text(fdic1,fdic2,labels=levels(factor(dd[,k])),col=seguentColor, cex=0.6)
c<-c+1
}
legend("bottomleft",names(dd)[dcat],pch=1,col=colors, cex=0.6)



#add ordinal qualitative variables. Ensure ordering is the correct

dordi<-c(18)


#reorder modalities: when required
dd[,dordi[1]] <- factor(dd[,dordi[1]], ordered=TRUE,  levels= c('Larghissimo','Grave','Lento/Largo','Larghetto','Adagio','Andante','Moderato','Allegro','Vivace','Presto','Prestissimo'))
levels(dd[,dordi[1]])
 [1] "Larghissimo" "Grave"       "Lento/Largo" "Larghetto"   "Adagio"      "Andante"     "Moderato"   
 [8] "Allegro"     "Vivace"      "Presto"      "Prestissimo"
c<-1
col<-length(dcat)+1
for(k in dordi){
  seguentColor<-colors[col]
  fdic1 = tapply(Psi[,eje1],dd[,k],mean)
  fdic2 = tapply(Psi[,eje2],dd[,k],mean) 
  
  #points(fdic1,fdic2,pch=16,col=seguentColor, labels=levels(dd[,k]))
  #connect modalities of qualitative variables
  lines(fdic1,fdic2,col="#000000")
  text(fdic1,fdic2,labels=levels(dd[,k]),col=seguentColor, cex=0.6)
  c<-c+1
  col<-col+1
}
legend("topleft",names(dd)[dordi],pch=19,col=colors[col:col+length(dordi)-1], cex=0.6)

NA
NA

All categorical variables together


X<-Phi[,eje1]
Y<-Phi[,eje2]

plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-1,1), ylim=c(-3,1))
#plot(X,Y,type="none",xlim=c(min(X,0),max(X,0)))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")

arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)


#all qualitative together
plot(Psi[,eje1],Psi[,eje2],type="n",xlim=c(-2,4), ylim=c(-2,2))
axis(side=1, pos= 0, labels = F, col="cyan")
axis(side=3, pos= 0, labels = F, col="cyan")
axis(side=2, pos= 0, labels = F, col="cyan")
axis(side=4, pos= 0, labels = F, col="cyan")
arrows(ze, ze, X, Y, length = 0.07,col="lightgray")
text(X,Y,labels=etiq,col="gray", cex=0.7)

#nominal qualitative variables

dcat<-c(3, 6, 8, 16, 17)
colors<-rainbow(length(dcat))

c<-1
for(k in dcat){
  seguentColor<-colors[c]
fdic1 = tapply(Psi[,eje1],dd[,k],mean)
fdic2 = tapply(Psi[,eje2],dd[,k],mean) 

text(fdic1,fdic2,labels=levels(factor(dd[,k])),col=seguentColor, cex=0.6)
c<-c+1
}
legend("bottomleft",names(dd)[dcat],pch=1,col=colors, cex=0.6)

#add ordinal qualitative variables. Ensure ordering is the correct

dordi<-c(18)


#reorder modalities: when required
dd[,dordi[1]] <- factor(dd[,dordi[1]], ordered=TRUE,  levels= c('Larghissimo','Grave','Lento/Largo','Larghetto','Adagio','Andante','Moderato','Allegro','Vivace','Presto','Prestissimo'))
levels(dd[,dordi[1]])
 [1] "Larghissimo" "Grave"       "Lento/Largo" "Larghetto"   "Adagio"      "Andante"     "Moderato"   
 [8] "Allegro"     "Vivace"      "Presto"      "Prestissimo"
c<-1
col<-length(dcat)+1
for(k in dordi){
  seguentColor<-colors[col]
  fdic1 = tapply(Psi[,eje1],dd[,k],mean)
  fdic2 = tapply(Psi[,eje2],dd[,k],mean) 
  
  #points(fdic1,fdic2,pch=16,col=seguentColor, labels=levels(dd[,k]))
  #connect modalities of qualitative variables
  lines(fdic1,fdic2,col="#000000")
  text(fdic1,fdic2,labels=levels(dd[,k]),col=seguentColor, cex=0.6)
  c<-c+1
  col<-col+1
}
legend("topleft",names(dd)[dordi],pch=19,col=colors[col:col+length(dordi)-1], cex=0.6)

Observations from the plot of centroids above:

  1. Seems like the slower songs (Grave, Lento, Larghetto, Adagio) are also on the right side of the plot, which are more acoustic, instrumental songs. Whereas the faster songs are on the left side.
  2. Songs that are more loud, energetic and probably contains swear words are related to heavy metal, grunge, goth, genres, or latin/latino songs.
  3. Disney, jazz, and classical songs are probably high on the acousticness scale, while new-age, ambient, sleep songs are high on the instrumentalness scale.
  4. Songs on the bottom left side are happy, danceable songs, but the genres vary from children songs to songs that are more known to be danceable, such as hip hop, r&b, kpop, disco, salsa, etc.

Coloring the PCA plot using categorical variables

We can also plot all the individuals in PC1 and PC2 as axes, and color-code it by categorical variables. We’ll examine one categorical variable: Explicitness


# PROJECTION OF ILLUSTRATIVE qualitative variables on individuals' map
varcat=factor(dd[,3])
plot(Psi[,1],Psi[,2],col=c("grey", "red")[varcat])
axis(side=1, pos= 0, labels = F, col="darkgray")
axis(side=3, pos= 0, labels = F, col="darkgray")
axis(side=2, pos= 0, labels = F, col="darkgray")
axis(side=4, pos= 0, labels = F, col="darkgray")
legend("bottomleft",levels(varcat),pch=1,col=c("grey", "red"), cex=0.6)


# Overproject THE CDG OF  LEVELS OF varcat
fdic1 = tapply(Psi[,1],varcat,mean)
fdic2 = tapply(Psi[,2],varcat,mean) 

text(fdic1,fdic2,labels=levels(factor(varcat)),col="cyan", cex=0.75)

Although there are not many explicit songs, we can see that the explicit songs tend to be on the left side of PC1.


# PROJECTION OF ILLUSTRATIVE qualitative variables on individuals' map
varcat=factor(dd[,18])
plot(Psi[,1],Psi[,2],col=rainbow(9)[varcat])
axis(side=1, pos= 0, labels = F, col="darkgray")
axis(side=3, pos= 0, labels = F, col="darkgray")
axis(side=2, pos= 0, labels = F, col="darkgray")
axis(side=4, pos= 0, labels = F, col="darkgray")
legend("bottomleft",levels(varcat),pch=1,col=rainbow(9), cex=0.6)


# Overproject THE CDG OF  LEVELS OF varcat
fdic1 = tapply(Psi[,1],varcat,mean)
fdic2 = tapply(Psi[,2],varcat,mean) 

text(fdic1,fdic2,labels=levels(factor(varcat)),col="cyan", cex=0.75)

From the tempo categories plot we see that the left side of PCA is the faster songs, and the right side are the slower songs.

Conclusion - What we learned

With the PCA method, we’re able to see the relationship between numerical variables and which ones account for the most variance. We are also able to plot the songs along the principal component axes and see that some genres have similar characteristics. Next, we’ll continue exploring these similarity (and dissimilarities) in Clustering.

LS0tCnRpdGxlOiAiU3BvdGlmeSBkYXRhIC0gUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyIKb3V0cHV0OgogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKIyMgSW50cm9kdWN0aW9uCgpJbiB0aGlzIHJlcG9ydCwgd2Ugd2lsbCBiZSBkb2luZyBhIFByaW5jaXBhbCBDb21wb25lbnQgYW5hbHlzaXMgYmFzZWQgb24gU3BvdGlmeSBkYXRhIG9uIHBvcHVsYXIgc29uZ3MuCgpBIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgaXMgYSB0ZWNobmlxdWUgdG8gYW5hbHl6ZSBsYXJnZSBkYXRhc2V0cyBjb250YWluaW5nIG11bHRpcGxlIGRpbWVuc2lvbnMvZmVhdHVyZXMuIFRoZSBwdXJwb3NlIGlzIHRvIGVuYWJsZSB2aXN1YWxpemF0aW9uIG9mIG11bHRpZGltZW5zaW9uYWwgZGF0YS4KClBDQSBpZGVudGlmaWVzIHRoZSBtYWluIGF4ZXMgb2YgdmFyaWFuY2Ugd2l0aGluIGEgZGF0YSBzZXQgYW5kIGFsbG93cyBmb3IgZWFzeSBkYXRhIGV4cGxvcmF0aW9uIHRvIHVuZGVyc3RhbmQgdGhlIGtleSB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgYW5kIHNwb3Qgb3V0bGllcnMuCgpUaGUgZ29hbCBpcyB0byBkZXRlcm1pbmUgd2hpY2ggdmFyaWFibGVzIGFyZSByZWxhdGVkIG9yIG5vdCByZWxhdGVkIHRvIGVhY2ggb3RoZXIuCgpgYGB7ciwgcmVzdWx0cz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoamFuaXRvcikKYGBgCgojIyBJbXBvcnRpbmcgdGhlIGRhdGEKCldlIGFyZSB1c2luZyBhIGRhdGFzZXQgb2YgcG9wdWxhciBzb25ncyBvbiBTcG90aWZ5LiBJbiB0aGUgcHJldmlvdXMgcmVwb3J0IG9uIHByZS1wcm9jZXNzaW5nLCBkZXNjcmlwdGl2ZSBhbmQgYml2YXJpYXRlIHN0YXRpc3RpY3MsIHdlIGhhdmUgZGVzY3JpYmVkIHRoaXMgZGF0YXNldCBhbmQgbWFkZSBzZXZlcmFsIHRyYW5zZm9ybWF0aW9ucy4KCltMaW5rIHRvIG9yaWdpbmFsIGRhdGFzZXRdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvbWFoYXJzaGlwYW5keWEvLXNwb3RpZnktdHJhY2tzLWRhdGFzZXQpCgpgYGB7cn0Kc2V0d2QoIn4vRG9jdW1lbnRzL2NsYXNzL3N0YXRzLWZpbmFsLXByb2plY3QvIikKZGQgPC0gcmVhZC5jc3YoImNsZWFuZWRkYXRhLmNzdiIpICU+JSBtdXRhdGUoCiAgdGltZV9zaWduYXR1cmUgPSBhcy5jaGFyYWN0ZXIodGltZV9zaWduYXR1cmUpCikKYGBgCgojIyBQZXJmb3JtaW5nIHRoZSBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzCgpXZSB1c2UgdGhlIGBwcmNvbXAoKWAgZnVuY3Rpb24uCgpUaGlzIHJldHVybnMgMyB0aGluZ3M6CgoxLiB4ID0+IGNvbnRhaW5zIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cyAoUENzKSBmb3IgZHJhd2luZyBhIGdyYXBoLiBXZSB3aWxsIGJlIHVzaW5nIHRoZSBmaXJzdCB0d28gY29sdW1ucyBpbiB4IHRvIGRyYXcgYSAyRCBwbG90IHRoYXQgdXNlcyB0aGUgZmlyc3QgMiBQQ3MuIFRoZSBudW1iZXIgb2YgUENzIGlzIGRldGVybWluZWQgYnkgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgdXNlZC4KCjIuIHNkZXYKCjMuIHJvdGF0aW9uCgpCZWxvdyBpcyB0aGUgY29kZSB0byBydW4gdGhlIHByY29tcCBmdW5jdGlvbiwgYW5kIGEgYmFzaWMgcGxvdCBvZiBwcmluY2lwYWwgY29tcG9uZW50IDEgYW5kIDIuCgpgYGB7cn0KIyBkZXRlcm1pbmUgd2hpY2ggb25lcyBhcmUgbnVtZXJpY2FsIHZhcmlhYmxlcwpudW1lcmljYWwgPC0gd2hpY2goc2FwcGx5KGRkLGlzLm51bWVyaWMpKQojIHByaW50IGJlbG93IHRvIHNlZSBpZiB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyBhcmUgY29ycmVjdGx5IGRldGVjdGVkCiMgbnVtZXJpY2FsCgojIHNhdmluZyB0aGUgbnVtZXJpY2FsIG9ic2VydmF0aW9ucyB0byAiZGNvbiIKZGNvbiA8LSBkZFssbnVtZXJpY2FsXQojIHByaW50IGJlbG93IHRvIHNlZSBpZiB2YXJpYWJsZXMgZGV0ZWN0ZWQgYXJlIGluZGVlZCBudW1lcmljYWwKIyBzYXBwbHkoZGNvbixjbGFzcykKCiMgTm93IHdlIGRvIGEgUFJJTkNJUEFMIENPTVBPTkVOVCBBTkFMWVNJUyBvbiB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcwpwY2EgPC0gcHJjb21wKGRjb24sIHNjYWxlPVRSVUUsIGNlbnRlciA9IFRSVUUpICMgY2VudGVyaW5nIGFuZCBzY2FsaW5nIHRydWUKcGMxIDwtIHBjYQoKcGxvdChwY2EkeFssMV0sIHBjYSR4WywyXSkKCmBgYAoKIyMjIFNjcmVlIHBsb3QKCldpdGggdGhlIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMsIHdlIGNhbiBhbHNvIGNvbXB1dGUgdGhlIFNjcmVlIHBsb3QsIHdoaWNoIGRpc3BsYXlzIHRoZSB2YXJpYW5jZSBhY2NvdW50ZWQgZm9yIGJ5IHRoZSBjb21wb25lbnRzLgoKYGBge3J9CnBjYS52YXIgPC0gcGNhJHNkZXZeMgpwY2EudmFyLnBlciA8LSByb3VuZChwY2EudmFyL3N1bShwY2EudmFyKSoxMDAsIDEpCnBjYS52YXIucGVyCmJhcnBsb3QocGNhLnZhci5wZXIsIG1haW49IlNjcmVlIFBsb3QiLCB4bGFiPSJQcmluY2lwYWwgQ29tcG9uZW50IiwgeWxhYj0iUGVyY2VudCBWYXJpYXRpb24iKQoKI0N1bW11bGF0ZWQgSW5lcnRpYSBpbiBzdWJzcGFjZXMsIGZyb20gZmlyc3QgcHJpbmNpcGFsIGNvbXBvbmVudCB0byB0aGUgMTF0aCBkaW1lbnNpb24gc3Vic3BhY2UKYmFycGxvdCgxMDAqY3Vtc3VtKHBjMSRzZGV2WzE6ZGltKGRjb24pWzJdXV4yKS9kaW0oZGNvbilbMl0pCnBlcmNJbmVyQWNjdW08LTEwMCpjdW1zdW0ocGMxJHNkZXZbMTpkaW0oZGNvbilbMl1dXjIpL2RpbShkY29uKVsyXQpwZXJjSW5lckFjY3VtCmBgYAoKRnJvbSB0aGlzIHdlIHNlZSB0aGF0IFByaW5jaXBhbCBjb21wb25lbnQgMSBhY2NvdW50cyBmb3Igb25seSAyNi45JSBvZiB0aGUgdmFyaWF0aW9uLiBBbmQgaW4gb3JkZXIgdG8gYWNjb3VudCBmb3IgODAlIG9mIHRoZSB2YXJpYXRpb24sIHdlIG5lZWQgdG8gaW5jbHVkZSBQQyAxLTcuCgpOZXh0IHdlIHdpbGwgc3RvcmUgdGhlIGVpZ2VudmFsdWVzLCBlaWdlbnZlY3RvcnMsIHByb2plY3Rpb25zIGFuZCBpbmNsdWRlIFBDIDEtNy4KCmBgYHtyLCByZXN1bHRzPUZBTFNFfQojIFNFTEVDVElPTiBPRiBUSEUgU0lOR0lGSUNBTlQgRElNRU5TSU9OUyAoa2VlcCA4MCUgb2YgdG90YWwgaW5lcnRpYSkKbmQgPSA3CnBjMSRyb3RhdGlvbgoKIyBTVE9SQUdFIE9GIFRIRSBFSUdFTlZBTFVFUywgRUlHRU5WRUNUT1JTIEFORCBQUk9KRUNUSU9OUyBJTiBUSEUgbmQgRElNRU5TSU9OUwpQc2kgPSBwYzEkeFssMTpuZF0KUHNpCgoKIyBTVE9SQUdFIE9GIExBQkVMUyBGT1IgSU5ESVZJRFVBTFMgQU5EIFZBUklBQkxFUwppZGVuID0gcm93Lm5hbWVzKGRjb24pCmV0aXEgPSBuYW1lcyhkY29uKSAjIGdldHRpbmcgbmFtZXMgb2YgbnVtZXJpY2FsIHZhcmlhYmxlcwp6ZSA9IHJlcCgwLGxlbmd0aChldGlxKSkgIyBXRSBXSUxMIE5FRUQgVEhJUyBWRUNUT1IgQUZURVJXQVJEUyBGT1IgVEhFIEdSQVBISUNTCmBgYAoKIyMgUGxvdHRpbmcgdGhlIGluZGl2aWR1YWxzIG9uIFBDMSBhbmQgUEMyIGF4ZXMKCmBgYHtyfQojIFBMT1QgT0YgSU5ESVZJRFVBTFMKCiNzZWxlY3QgeW91ciBheGlzCiNlamUxPC0yCmVqZTE8LTEKI2VqZTI8LTMKZWplMjwtMgoKcGxvdChQc2lbLGVqZTFdLFBzaVssZWplMl0pCnRleHQoUHNpWyxlamUxXSxQc2lbLGVqZTJdLGxhYmVscz1pZGVuLCBjZXg9MC41KQpheGlzKHNpZGU9MSwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MywgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MiwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9NCwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpgYGAKCiMjIFBsb3R0aW5nIHByb2plY3Rpb24gb2YgdmFyaWFibGVzLCBQQzEgYW5kIFBDMgoKV2Ugd2lsbCBjcmVhdGUgYSBsb2FkaW5ncyBwbG90IHRvIHNlZSB3aGljaCB2YXJpYWJsZXMgbW9zdCBpbmZsdWVuY2UgUEMxIGFuZCBQQzIuCgpgYGB7cn0KI1Byb2plY3Rpb24gb2YgdmFyaWFibGVzCgpQaGkgPSBjb3IoZGNvbixQc2kpClBoaQoKWDwtUGhpWyxlamUxXQpZPC1QaGlbLGVqZTJdCgojem9vbXMKcGxvdChQc2lbLGVqZTFdLFBzaVssZWplMl0sdHlwZT0ibiIseGxpbT1jKG1pbihYLDApLG1heChYLDApKSwgeWxpbT1jKC0xLDEpKQpheGlzKHNpZGU9MSwgcG9zPSAwLCBsYWJlbHMgPSBGKQpheGlzKHNpZGU9MywgcG9zPSAwLCBsYWJlbHMgPSBGKQpheGlzKHNpZGU9MiwgcG9zPSAwLCBsYWJlbHMgPSBGKQpheGlzKHNpZGU9NCwgcG9zPSAwLCBsYWJlbHMgPSBGKQphcnJvd3MoemUsIHplLCBYLCBZLCBsZW5ndGggPSAwLjA3LGNvbD0iYmx1ZSIpCnRleHQoWCxZLGxhYmVscz1ldGlxLGNvbD0iZGFya2JsdWUiLCBjZXg9MC43KQpgYGAKCiMjIyBPYnNlcnZhdGlvbnM6CgpCYXNlZCBvbiB0aGUgIExvYWRpbmdzIHBsb3QsIHdlIHNlZSB0aGF0IHRoZSB2YXJpYWJsZXMgdGhhdCBtb3N0IGluZmx1ZW5jZSBQQzEgYXJlIGluc3RydW1lbnRhbG5lc3MsIGVuZXJneSwgbG91ZG5lc3MsIGFuZCBhY291c3RpY25lc3MuCgpUaGUgdmFyaWFibGVzIHRoYXQgbW9zdCBpbmZsdWVuY2UgUEMyIGFyZSBkdXJhdGlvbi4KCkZyb20gdGhpcyBwbG90IHdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZXNlIHZhcmlhYmxlcyBtYXkgYmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkOgotIExvdWRuZXNzIGFuZCBlbmVyZ3kKLSBWYWxlbmNlIGFuZCBkYW5jZWFiaWxpdHkKLSBJbnN0cnVtZW50YWxuZXNzIGFuZCBhY291c3RpY25lc3MKClRoZXNlIHZhcmlhYmxlcyBtYXkgYmUgbmVnYXRpdmVseSBjb3JyZWxhdGVkOgotIExvdWRuZXNzIGFuZCBpbnN0cnVtZW50YWxuZXNzCi0gTG91ZG5lc3MgYW5kIGFjb3VzdGljbmVzcwotIEVuZXJneSBhbmQgYWNvdXN0aWNuZXNzCi0gVmFsZW5jZSBhbmQgaW5zdHJ1bWVudGFsbmVzcwotIFRlbXBvIGFuZCBhY291c3RpY25lc3MKClRoZXNlIHZhcmlhYmxlcyBhcmUgb3J0aG9nb25hbC9tYXkgbm90IGJlIHZlcnkgcmVsYXRlZCB0byBlYWNoIG90aGVyOgotIER1cmF0aW9uIHZzIGxvdWRuZXNzCi0gRHVyYXRpb24gdnMgaW5zdHJ1bWVudGFsbmVzcwotIFNwZWVjaGluZXNzIHZzIGFjb3VzdGljbmVzcwoKV2UgY2FuIGFsc28gdHJ5IHBsb3R0aW5nIHRoZSBQQzEgYWdhaW5zdCBQQzMgdG8gc2VlIHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgbm90IHNvIGNsZWFybHkgdmlzaWJsZSBoZXJlLgoKIyMgUGxvdHRpbmcgcHJvamVjdGlvbiBvZiB2YXJpYWJsZXMsIFBDMSBhbmQgUEMzCgpTaW5jZSBQQzEgYW5kIFBDMiBvbmx5IGFjY291bnRzIGZvciA0MSUgb2YgdGhlIHZhcmlhdGlvbiwgd2Ugc2hvdWxkIGFsc28gcGxvdCBwcm9qZWN0aW9uIG9mIHZhcmlhYmxlcyBpbiBQQzEgYW5kIFBDMy4KCmBgYHtyfQojUHJvamVjdGlvbiBvZiB2YXJpYWJsZXMKClBoaSA9IGNvcihkY29uLFBzaSkKUGhpCgplamUzIDwtIDMKClg8LVBoaVssZWplMV0KWTwtUGhpWyxlamUzXQoKI3pvb21zCnBsb3QoUHNpWyxlamUxXSxQc2lbLGVqZTNdLHR5cGU9Im4iLHhsaW09YyhtaW4oWCwwKSxtYXgoWCwwKSksIHlsaW09YygtMSwxKSkKYXhpcyhzaWRlPTEsIHBvcz0gMCwgbGFiZWxzID0gRikKYXhpcyhzaWRlPTMsIHBvcz0gMCwgbGFiZWxzID0gRikKYXhpcyhzaWRlPTIsIHBvcz0gMCwgbGFiZWxzID0gRikKYXhpcyhzaWRlPTQsIHBvcz0gMCwgbGFiZWxzID0gRikKYXJyb3dzKHplLCB6ZSwgWCwgWSwgbGVuZ3RoID0gMC4wNyxjb2w9ImJsdWUiKQp0ZXh0KFgsWSxsYWJlbHM9ZXRpcSxjb2w9ImRhcmtibHVlIiwgY2V4PTAuNykKYGBgCgpCYXNlZCBvbiB0aGUgYWJvdmUgTG9hZGluZ3MgcGxvdCwgd2Ugc2VlIHRoYXQgdGhlIHZhcmlhYmxlcyB0aGF0IG1vc3QgaW5mbHVlbmNlIFBDMyBhcmUgcG9wdWxhcml0eSwgbGl2ZW5lc3MgYW5kIGR1cmF0aW9uLgoKSXQgYWxzbyBzZWVtcyBsaWtlIFBvcHVsYXJpdHkgYW5kIGxpdmVuZXNzIGFyZSBjbG9zZWx5IHJlbGF0ZWQsIHdoaWxlIGl0IG1heSBiZSBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCBkdXJhdGlvbi4KCldlIGNhbiBhbHNvIHNlZSB0aGF0IHNwZWVjaGluZXNzIGlzIGNsb3NlciB0byBkYW5jZWFiaWxpdHkuCgojIyBGaW5kaW5nIENlbnRyb2lkcwoKV2UgY2FuIGFsc28gZmluZCB0aGUgY2VudHJvaWRzIG9mIG1vZGFsaXRpZXMgaW4gY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB1c2luZyB0aGUgY29kZSBiZWxvdy4KClRoYXQgbG9va3MgYSBiaXQgY3Jvd2RlZCwgc28gbGV0J3MgdHJ5IGxvb2tpbmcgYXQgZWFjaCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMnIG1vZGFsaXRpZXMgb25lIGJ5IG9uZS4KCiMjIyBFeHBsaWNpdAoKRXhwbGljaXQgc29uZ3Mvc29uZ3MgdGhhdCBjb250YWluIHN3ZWFyIHdvcmRzIGFyZSBtb3JlIGxpa2VseSB0byBiZSBlbmVyZ2V0aWMuIFRoaXMgY291bGQgYmUgTGF0aW5vIHNvbmdzIG9yIHJhcCBzb25ncy4KCmBgYHtyfQoKWDwtUGhpWyxlamUxXQpZPC1QaGlbLGVqZTJdCgojYWxsIHF1YWxpdGF0aXZlIHRvZ2V0aGVyCnBsb3QoUHNpWyxlamUxXSxQc2lbLGVqZTJdLHR5cGU9Im4iLHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpKQpheGlzKHNpZGU9MSwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MywgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MiwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9NCwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQphcnJvd3MoemUsIHplLCBYLCBZLCBsZW5ndGggPSAwLjA3LGNvbD0ibGlnaHRncmF5IikKdGV4dChYLFksbGFiZWxzPWV0aXEsY29sPSJncmF5IiwgY2V4PTAuNykKCiNub21pbmFsIHF1YWxpdGF0aXZlIHZhcmlhYmxlcwoKZGNhdDwtYygzKQpjb2xvcnM8LXJhaW5ib3cobGVuZ3RoKGRjYXQpKQoKYzwtMQpmb3IoayBpbiBkY2F0KXsKICBzZWd1ZW50Q29sb3I8LWNvbG9yc1tjXQpmZGljMSA9IHRhcHBseShQc2lbLGVqZTFdLGRkWyxrXSxtZWFuKQpmZGljMiA9IHRhcHBseShQc2lbLGVqZTJdLGRkWyxrXSxtZWFuKSAKCnRleHQoZmRpYzEsZmRpYzIsbGFiZWxzPWxldmVscyhmYWN0b3IoZGRbLGtdKSksY29sPXNlZ3VlbnRDb2xvciwgY2V4PTAuNikKYzwtYysxCn0KbGVnZW5kKCJib3R0b21sZWZ0IixuYW1lcyhkZClbZGNhdF0scGNoPTEsY29sPWNvbG9ycywgY2V4PTAuNikKCgpgYGAKCiMjIyBLZXkKCldoZW4gd2UgcGxvdCB0aGUgbW9kYWxpdHkgb2Yga2V5cywgaXQncyBpbnRlcmVzdGluZyBiZWNhdXNlIHlvdSBjYW4gc2VlIHRoYXQgc29tZSBrZXlzIGFyZSBhc3NvY2lhdGVkIHdpdGggZW5lcmdldGljIG11c2ljLCBzdWNoIGFzIHRoZSAiYmxhY2siIGtleXMgRiMsIEcjIGFuZCBDIy4KClNvbWUgb3RoZXIga2V5cyBzdWNoIGFzIHRoZSBtb3JlIHBvcHVsYXIgQywgRCBhbmQgRyBhcmUgYXNzb2NpYXRlZCB3aXRoIG1vcmUgYWNvdXN0aWMgbXVzaWMuCgpFIGFuZCBBIGFyZSBtb3JlIGRhbmNlYWJsZSwgc3BlZWNoeSBzb25ncyAocHJvYmFibHksIGNoaWxkcmVuJ3MgbXVzaWMpPwoKSXQgZml0cyB0aGlzIGRlc2NyaXB0aW9uIHRoYXQga2V5IHNpZ25hdHVyZXMgaGF2ZSBtdXNpYyBjaGFyYWN0ZXJpc3RpY3M6ICJOb2lzeSBzaG91dHMgb2Ygam95LCBsYXVnaGluZyBwbGVhc3VyZSBhbmQgbm90IHlldCBjb21wbGV0ZSwgZnVsbCBkZWxpZ2h0IGxpZXMgaW4gRSBNYWpvci4iCgpgYGB7cn0KI2FsbCBxdWFsaXRhdGl2ZSB0b2dldGhlcgpwbG90KFBzaVssZWplMV0sUHNpWyxlamUyXSx0eXBlPSJuIix4bGltPWMoLTEsMSksIHlsaW09YygtMSwxKSkKYXhpcyhzaWRlPTEsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTMsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTIsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTQsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXJyb3dzKHplLCB6ZSwgWCwgWSwgbGVuZ3RoID0gMC4wNyxjb2w9ImxpZ2h0Z3JheSIpCnRleHQoWCxZLGxhYmVscz1ldGlxLGNvbD0iZ3JheSIsIGNleD0wLjcpCgojbm9taW5hbCBxdWFsaXRhdGl2ZSB2YXJpYWJsZXMKCmRjYXQ8LWMoNikKY29sb3JzPC1yYWluYm93KGxlbmd0aChkY2F0KSkKCmM8LTEKZm9yKGsgaW4gZGNhdCl7CiAgc2VndWVudENvbG9yPC1jb2xvcnNbY10KZmRpYzEgPSB0YXBwbHkoUHNpWyxlamUxXSxkZFssa10sbWVhbikKZmRpYzIgPSB0YXBwbHkoUHNpWyxlamUyXSxkZFssa10sbWVhbikgCgp0ZXh0KGZkaWMxLGZkaWMyLGxhYmVscz1sZXZlbHMoZmFjdG9yKGRkWyxrXSkpLGNvbD1zZWd1ZW50Q29sb3IsIGNleD0wLjYpCmM8LWMrMQp9CmxlZ2VuZCgiYm90dG9tbGVmdCIsbmFtZXMoZGQpW2RjYXRdLHBjaD0xLGNvbD1jb2xvcnMsIGNleD0wLjYpCgoKYGBgCgojIyMgTW9kZQoKYGBge3J9CiNhbGwgcXVhbGl0YXRpdmUgdG9nZXRoZXIKcGxvdChQc2lbLGVqZTFdLFBzaVssZWplMl0sdHlwZT0ibiIseGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSkpCmF4aXMoc2lkZT0xLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT0zLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT0yLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT00LCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmFycm93cyh6ZSwgemUsIFgsIFksIGxlbmd0aCA9IDAuMDcsY29sPSJsaWdodGdyYXkiKQp0ZXh0KFgsWSxsYWJlbHM9ZXRpcSxjb2w9ImdyYXkiLCBjZXg9MC43KQoKI25vbWluYWwgcXVhbGl0YXRpdmUgdmFyaWFibGVzCgpkY2F0PC1jKDgpCmNvbG9yczwtcmFpbmJvdyhsZW5ndGgoZGNhdCkpCgpjPC0xCmZvcihrIGluIGRjYXQpewogIHNlZ3VlbnRDb2xvcjwtY29sb3JzW2NdCmZkaWMxID0gdGFwcGx5KFBzaVssZWplMV0sZGRbLGtdLG1lYW4pCmZkaWMyID0gdGFwcGx5KFBzaVssZWplMl0sZGRbLGtdLG1lYW4pIAoKdGV4dChmZGljMSxmZGljMixsYWJlbHM9bGV2ZWxzKGZhY3RvcihkZFssa10pKSxjb2w9c2VndWVudENvbG9yLCBjZXg9MC42KQpjPC1jKzEKfQpsZWdlbmQoImJvdHRvbWxlZnQiLG5hbWVzKGRkKVtkY2F0XSxwY2g9MSxjb2w9Y29sb3JzLCBjZXg9MC42KQoKCmBgYAojIyMgTXVsdGlwbGUgYXJ0aXN0cwoKVGhpcyBwbG90IHNob3dzIHRoYXQgc29uZ3MgdGhhdCBoYXZlIG11bHRpcGxlIGFydGlzdHMgYXJlIG1vcmUgbGlrZWx5IHRvIGJlIGFjb3VzdGljLgoKYGBge3J9CiNhbGwgcXVhbGl0YXRpdmUgdG9nZXRoZXIKcGxvdChQc2lbLGVqZTFdLFBzaVssZWplMl0sdHlwZT0ibiIseGxpbT1jKC0xLDMpLCB5bGltPWMoLTIsMikpCmF4aXMoc2lkZT0xLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT0zLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT0yLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT00LCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmFycm93cyh6ZSwgemUsIFgsIFksIGxlbmd0aCA9IDAuMDcsY29sPSJsaWdodGdyYXkiKQp0ZXh0KFgsWSxsYWJlbHM9ZXRpcSxjb2w9ImdyYXkiLCBjZXg9MC43KQoKI25vbWluYWwgcXVhbGl0YXRpdmUgdmFyaWFibGVzCgpkY2F0PC1jKDE3KQpjb2xvcnM8LXJhaW5ib3cobGVuZ3RoKGRjYXQpKQoKYzwtMQpmb3IoayBpbiBkY2F0KXsKICBzZWd1ZW50Q29sb3I8LWNvbG9yc1tjXQpmZGljMSA9IHRhcHBseShQc2lbLGVqZTFdLGRkWyxrXSxtZWFuKQpmZGljMiA9IHRhcHBseShQc2lbLGVqZTJdLGRkWyxrXSxtZWFuKSAKCnRleHQoZmRpYzEsZmRpYzIsbGFiZWxzPWxldmVscyhmYWN0b3IoZGRbLGtdKSksY29sPXNlZ3VlbnRDb2xvciwgY2V4PTAuNikKYzwtYysxCn0KbGVnZW5kKCJib3R0b21sZWZ0IixuYW1lcyhkZClbZGNhdF0scGNoPTEsY29sPWNvbG9ycywgY2V4PTAuNikKCgpgYGAKCiMjIyBUZW1wbyBjYXRlZ29yeSAoYW4gb3JkaW5hbCB2YXJpYWJsZSkKClNsb3dlciBzb25ncyBhcmUgb24gdGhlIHJpZ2h0IHNpZGUsIGFuZCBmYXN0ZXIgc29uZ3MgYXJlIG9uIHRoZSBsZWZ0LgoKYGBge3J9CgojYWxsIHF1YWxpdGF0aXZlIHRvZ2V0aGVyCnBsb3QoUHNpWyxlamUxXSxQc2lbLGVqZTJdLHR5cGU9Im4iLHhsaW09YygtMSwzKSwgeWxpbT1jKC0xLDEpKQpheGlzKHNpZGU9MSwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MywgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MiwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9NCwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQphcnJvd3MoemUsIHplLCBYLCBZLCBsZW5ndGggPSAwLjA3LGNvbD0ibGlnaHRncmF5IikKdGV4dChYLFksbGFiZWxzPWV0aXEsY29sPSJncmF5IiwgY2V4PTAuNykKCgojYWRkIG9yZGluYWwgcXVhbGl0YXRpdmUgdmFyaWFibGVzLiBFbnN1cmUgb3JkZXJpbmcgaXMgdGhlIGNvcnJlY3QKCmRvcmRpPC1jKDE4KQoKCiNyZW9yZGVyIG1vZGFsaXRpZXM6IHdoZW4gcmVxdWlyZWQKZGRbLGRvcmRpWzFdXSA8LSBmYWN0b3IoZGRbLGRvcmRpWzFdXSwgb3JkZXJlZD1UUlVFLCAgbGV2ZWxzPSBjKCdMYXJnaGlzc2ltbycsJ0dyYXZlJywnTGVudG8vTGFyZ28nLCdMYXJnaGV0dG8nLCdBZGFnaW8nLCdBbmRhbnRlJywnTW9kZXJhdG8nLCdBbGxlZ3JvJywnVml2YWNlJywnUHJlc3RvJywnUHJlc3Rpc3NpbW8nKSkKbGV2ZWxzKGRkWyxkb3JkaVsxXV0pCgpjPC0xCmNvbDwtbGVuZ3RoKGRjYXQpKzEKZm9yKGsgaW4gZG9yZGkpewogIHNlZ3VlbnRDb2xvcjwtY29sb3JzW2NvbF0KICBmZGljMSA9IHRhcHBseShQc2lbLGVqZTFdLGRkWyxrXSxtZWFuKQogIGZkaWMyID0gdGFwcGx5KFBzaVssZWplMl0sZGRbLGtdLG1lYW4pIAogIAogICNwb2ludHMoZmRpYzEsZmRpYzIscGNoPTE2LGNvbD1zZWd1ZW50Q29sb3IsIGxhYmVscz1sZXZlbHMoZGRbLGtdKSkKICAjY29ubmVjdCBtb2RhbGl0aWVzIG9mIHF1YWxpdGF0aXZlIHZhcmlhYmxlcwogIGxpbmVzKGZkaWMxLGZkaWMyLGNvbD0iIzAwMDAwMCIpCiAgdGV4dChmZGljMSxmZGljMixsYWJlbHM9bGV2ZWxzKGRkWyxrXSksY29sPXNlZ3VlbnRDb2xvciwgY2V4PTAuNikKICBjPC1jKzEKICBjb2w8LWNvbCsxCn0KbGVnZW5kKCJ0b3BsZWZ0IixuYW1lcyhkZClbZG9yZGldLHBjaD0xOSxjb2w9Y29sb3JzW2NvbDpjb2wrbGVuZ3RoKGRvcmRpKS0xXSwgY2V4PTAuNikKCgpgYGAKIyMjIEFsbCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKCmBgYHtyfQoKI2FsbCBxdWFsaXRhdGl2ZSB0b2dldGhlcgpwbG90KFBzaVssZWplMV0sUHNpWyxlamUyXSx0eXBlPSJuIix4bGltPWMoLTEsMiksIHlsaW09YygtMS41LDEpKQpheGlzKHNpZGU9MSwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MywgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9MiwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQpheGlzKHNpZGU9NCwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImN5YW4iKQphcnJvd3MoemUsIHplLCBYLCBZLCBsZW5ndGggPSAwLjA3LGNvbD0ibGlnaHRncmF5IikKdGV4dChYLFksbGFiZWxzPWV0aXEsY29sPSJncmF5IiwgY2V4PTAuNykKCiNub21pbmFsIHF1YWxpdGF0aXZlIHZhcmlhYmxlcwoKZGNhdDwtYygzLDYsOCwxNykKY29sb3JzPC1yYWluYm93KGxlbmd0aChkY2F0KSkKCmM8LTEKZm9yKGsgaW4gZGNhdCl7CiAgc2VndWVudENvbG9yPC1jb2xvcnNbY10KZmRpYzEgPSB0YXBwbHkoUHNpWyxlamUxXSxkZFssa10sbWVhbikKZmRpYzIgPSB0YXBwbHkoUHNpWyxlamUyXSxkZFssa10sbWVhbikgCgp0ZXh0KGZkaWMxLGZkaWMyLGxhYmVscz1sZXZlbHMoZmFjdG9yKGRkWyxrXSkpLGNvbD1zZWd1ZW50Q29sb3IsIGNleD0wLjYpCmM8LWMrMQp9CmxlZ2VuZCgiYm90dG9tbGVmdCIsbmFtZXMoZGQpW2RjYXRdLHBjaD0xLGNvbD1jb2xvcnMsIGNleD0wLjYpCmBgYAoKIyMjIEFsbCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgKGluY2x1ZGluZyB0ZW1wbykgZXhjZXB0IGdlbnJlCgpgYGB7cn0KCiNhbGwgcXVhbGl0YXRpdmUgdG9nZXRoZXIKcGxvdChQc2lbLGVqZTFdLFBzaVssZWplMl0sdHlwZT0ibiIseGxpbT1jKC0xLDMpLCB5bGltPWMoLTIsMikpCmF4aXMoc2lkZT0xLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT0zLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT0yLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmF4aXMoc2lkZT00LCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iY3lhbiIpCmFycm93cyh6ZSwgemUsIFgsIFksIGxlbmd0aCA9IDAuMDcsY29sPSJsaWdodGdyYXkiKQp0ZXh0KFgsWSxsYWJlbHM9ZXRpcSxjb2w9ImdyYXkiLCBjZXg9MC43KQoKI25vbWluYWwgcXVhbGl0YXRpdmUgdmFyaWFibGVzCgpkY2F0PC1jKDMsNiw4LDE3KQpjb2xvcnM8LXJhaW5ib3cobGVuZ3RoKGRjYXQpKQoKYzwtMQpmb3IoayBpbiBkY2F0KXsKICBzZWd1ZW50Q29sb3I8LWNvbG9yc1tjXQpmZGljMSA9IHRhcHBseShQc2lbLGVqZTFdLGRkWyxrXSxtZWFuKQpmZGljMiA9IHRhcHBseShQc2lbLGVqZTJdLGRkWyxrXSxtZWFuKSAKCnRleHQoZmRpYzEsZmRpYzIsbGFiZWxzPWxldmVscyhmYWN0b3IoZGRbLGtdKSksY29sPXNlZ3VlbnRDb2xvciwgY2V4PTAuNikKYzwtYysxCn0KbGVnZW5kKCJib3R0b21sZWZ0IixuYW1lcyhkZClbZGNhdF0scGNoPTEsY29sPWNvbG9ycywgY2V4PTAuNikKCgoKI2FkZCBvcmRpbmFsIHF1YWxpdGF0aXZlIHZhcmlhYmxlcy4gRW5zdXJlIG9yZGVyaW5nIGlzIHRoZSBjb3JyZWN0Cgpkb3JkaTwtYygxOCkKCgojcmVvcmRlciBtb2RhbGl0aWVzOiB3aGVuIHJlcXVpcmVkCmRkWyxkb3JkaVsxXV0gPC0gZmFjdG9yKGRkWyxkb3JkaVsxXV0sIG9yZGVyZWQ9VFJVRSwgIGxldmVscz0gYygnTGFyZ2hpc3NpbW8nLCdHcmF2ZScsJ0xlbnRvL0xhcmdvJywnTGFyZ2hldHRvJywnQWRhZ2lvJywnQW5kYW50ZScsJ01vZGVyYXRvJywnQWxsZWdybycsJ1ZpdmFjZScsJ1ByZXN0bycsJ1ByZXN0aXNzaW1vJykpCmxldmVscyhkZFssZG9yZGlbMV1dKQoKYzwtMQpjb2w8LWxlbmd0aChkY2F0KSsxCmZvcihrIGluIGRvcmRpKXsKICBzZWd1ZW50Q29sb3I8LWNvbG9yc1tjb2xdCiAgZmRpYzEgPSB0YXBwbHkoUHNpWyxlamUxXSxkZFssa10sbWVhbikKICBmZGljMiA9IHRhcHBseShQc2lbLGVqZTJdLGRkWyxrXSxtZWFuKSAKICAKICAjcG9pbnRzKGZkaWMxLGZkaWMyLHBjaD0xNixjb2w9c2VndWVudENvbG9yLCBsYWJlbHM9bGV2ZWxzKGRkWyxrXSkpCiAgI2Nvbm5lY3QgbW9kYWxpdGllcyBvZiBxdWFsaXRhdGl2ZSB2YXJpYWJsZXMKICBsaW5lcyhmZGljMSxmZGljMixjb2w9IiMwMDAwMDAiKQogIHRleHQoZmRpYzEsZmRpYzIsbGFiZWxzPWxldmVscyhkZFssa10pLGNvbD1zZWd1ZW50Q29sb3IsIGNleD0wLjYpCiAgYzwtYysxCiAgY29sPC1jb2wrMQp9CmxlZ2VuZCgidG9wbGVmdCIsbmFtZXMoZGQpW2RvcmRpXSxwY2g9MTksY29sPWNvbG9yc1tjb2w6Y29sK2xlbmd0aChkb3JkaSktMV0sIGNleD0wLjYpCgoKYGBgCgojIyMgQWxsIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0b2dldGhlcgoKYGBge3J9CgpYPC1QaGlbLGVqZTFdClk8LVBoaVssZWplMl0KCnBsb3QoUHNpWyxlamUxXSxQc2lbLGVqZTJdLHR5cGU9Im4iLHhsaW09YygtMSwxKSwgeWxpbT1jKC0zLDEpKQojcGxvdChYLFksdHlwZT0ibm9uZSIseGxpbT1jKG1pbihYLDApLG1heChYLDApKSkKYXhpcyhzaWRlPTEsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTMsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTIsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTQsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKCmFycm93cyh6ZSwgemUsIFgsIFksIGxlbmd0aCA9IDAuMDcsY29sPSJsaWdodGdyYXkiKQp0ZXh0KFgsWSxsYWJlbHM9ZXRpcSxjb2w9ImdyYXkiLCBjZXg9MC43KQoKI2FsbCBxdWFsaXRhdGl2ZSB0b2dldGhlcgpwbG90KFBzaVssZWplMV0sUHNpWyxlamUyXSx0eXBlPSJuIix4bGltPWMoLTIsNCksIHlsaW09YygtMiwyKSkKYXhpcyhzaWRlPTEsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTMsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTIsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXhpcyhzaWRlPTQsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJjeWFuIikKYXJyb3dzKHplLCB6ZSwgWCwgWSwgbGVuZ3RoID0gMC4wNyxjb2w9ImxpZ2h0Z3JheSIpCnRleHQoWCxZLGxhYmVscz1ldGlxLGNvbD0iZ3JheSIsIGNleD0wLjcpCgojbm9taW5hbCBxdWFsaXRhdGl2ZSB2YXJpYWJsZXMKCmRjYXQ8LWMoMywgNiwgOCwgMTYsIDE3KQpjb2xvcnM8LXJhaW5ib3cobGVuZ3RoKGRjYXQpKQoKYzwtMQpmb3IoayBpbiBkY2F0KXsKICBzZWd1ZW50Q29sb3I8LWNvbG9yc1tjXQpmZGljMSA9IHRhcHBseShQc2lbLGVqZTFdLGRkWyxrXSxtZWFuKQpmZGljMiA9IHRhcHBseShQc2lbLGVqZTJdLGRkWyxrXSxtZWFuKSAKCnRleHQoZmRpYzEsZmRpYzIsbGFiZWxzPWxldmVscyhmYWN0b3IoZGRbLGtdKSksY29sPXNlZ3VlbnRDb2xvciwgY2V4PTAuNikKYzwtYysxCn0KbGVnZW5kKCJib3R0b21sZWZ0IixuYW1lcyhkZClbZGNhdF0scGNoPTEsY29sPWNvbG9ycywgY2V4PTAuNikKCiNhZGQgb3JkaW5hbCBxdWFsaXRhdGl2ZSB2YXJpYWJsZXMuIEVuc3VyZSBvcmRlcmluZyBpcyB0aGUgY29ycmVjdAoKZG9yZGk8LWMoMTgpCgoKI3Jlb3JkZXIgbW9kYWxpdGllczogd2hlbiByZXF1aXJlZApkZFssZG9yZGlbMV1dIDwtIGZhY3RvcihkZFssZG9yZGlbMV1dLCBvcmRlcmVkPVRSVUUsICBsZXZlbHM9IGMoJ0xhcmdoaXNzaW1vJywnR3JhdmUnLCdMZW50by9MYXJnbycsJ0xhcmdoZXR0bycsJ0FkYWdpbycsJ0FuZGFudGUnLCdNb2RlcmF0bycsJ0FsbGVncm8nLCdWaXZhY2UnLCdQcmVzdG8nLCdQcmVzdGlzc2ltbycpKQpsZXZlbHMoZGRbLGRvcmRpWzFdXSkKCmM8LTEKY29sPC1sZW5ndGgoZGNhdCkrMQpmb3IoayBpbiBkb3JkaSl7CiAgc2VndWVudENvbG9yPC1jb2xvcnNbY29sXQogIGZkaWMxID0gdGFwcGx5KFBzaVssZWplMV0sZGRbLGtdLG1lYW4pCiAgZmRpYzIgPSB0YXBwbHkoUHNpWyxlamUyXSxkZFssa10sbWVhbikgCiAgCiAgI3BvaW50cyhmZGljMSxmZGljMixwY2g9MTYsY29sPXNlZ3VlbnRDb2xvciwgbGFiZWxzPWxldmVscyhkZFssa10pKQogICNjb25uZWN0IG1vZGFsaXRpZXMgb2YgcXVhbGl0YXRpdmUgdmFyaWFibGVzCiAgbGluZXMoZmRpYzEsZmRpYzIsY29sPSIjMDAwMDAwIikKICB0ZXh0KGZkaWMxLGZkaWMyLGxhYmVscz1sZXZlbHMoZGRbLGtdKSxjb2w9c2VndWVudENvbG9yLCBjZXg9MC42KQogIGM8LWMrMQogIGNvbDwtY29sKzEKfQpsZWdlbmQoInRvcGxlZnQiLG5hbWVzKGRkKVtkb3JkaV0scGNoPTE5LGNvbD1jb2xvcnNbY29sOmNvbCtsZW5ndGgoZG9yZGkpLTFdLCBjZXg9MC42KQpgYGAKCk9ic2VydmF0aW9ucyBmcm9tIHRoZSBwbG90IG9mIGNlbnRyb2lkcyBhYm92ZToKCjEuIFNlZW1zIGxpa2UgdGhlIHNsb3dlciBzb25ncyAoR3JhdmUsIExlbnRvLCBMYXJnaGV0dG8sIEFkYWdpbykgYXJlIGFsc28gb24gdGhlIHJpZ2h0IHNpZGUgb2YgdGhlIHBsb3QsIHdoaWNoIGFyZSBtb3JlIGFjb3VzdGljLCBpbnN0cnVtZW50YWwgc29uZ3MuIFdoZXJlYXMgdGhlIGZhc3RlciBzb25ncyBhcmUgb24gdGhlIGxlZnQgc2lkZS4KMi4gU29uZ3MgdGhhdCBhcmUgbW9yZSBsb3VkLCBlbmVyZ2V0aWMgYW5kIHByb2JhYmx5IGNvbnRhaW5zIHN3ZWFyIHdvcmRzIGFyZSByZWxhdGVkIHRvIGhlYXZ5IG1ldGFsLCBncnVuZ2UsIGdvdGgsIGdlbnJlcywgb3IgbGF0aW4vbGF0aW5vIHNvbmdzLgozLiBEaXNuZXksIGphenosIGFuZCBjbGFzc2ljYWwgc29uZ3MgYXJlIHByb2JhYmx5IGhpZ2ggb24gdGhlIGFjb3VzdGljbmVzcyBzY2FsZSwgd2hpbGUgbmV3LWFnZSwgYW1iaWVudCwgc2xlZXAgc29uZ3MgYXJlIGhpZ2ggb24gdGhlIGluc3RydW1lbnRhbG5lc3Mgc2NhbGUuCjQuIFNvbmdzIG9uIHRoZSBib3R0b20gbGVmdCBzaWRlIGFyZSBoYXBweSwgZGFuY2VhYmxlIHNvbmdzLCBidXQgdGhlIGdlbnJlcyB2YXJ5IGZyb20gY2hpbGRyZW4gc29uZ3MgdG8gc29uZ3MgdGhhdCBhcmUgbW9yZSBrbm93biB0byBiZSBkYW5jZWFibGUsIHN1Y2ggYXMgaGlwIGhvcCwgciZiLCBrcG9wLCBkaXNjbywgc2Fsc2EsIGV0Yy4KCiMjIENvbG9yaW5nIHRoZSBQQ0EgcGxvdCB1c2luZyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKCldlIGNhbiBhbHNvIHBsb3QgYWxsIHRoZSBpbmRpdmlkdWFscyBpbiBQQzEgYW5kIFBDMiBhcyBheGVzLCBhbmQgY29sb3ItY29kZSBpdCBieSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuCldlJ2xsIGV4YW1pbmUgb25lIGNhdGVnb3JpY2FsIHZhcmlhYmxlOiBFeHBsaWNpdG5lc3MKCmBgYHtyfQoKIyBQUk9KRUNUSU9OIE9GIElMTFVTVFJBVElWRSBxdWFsaXRhdGl2ZSB2YXJpYWJsZXMgb24gaW5kaXZpZHVhbHMnIG1hcAp2YXJjYXQ9ZmFjdG9yKGRkWywzXSkKcGxvdChQc2lbLDFdLFBzaVssMl0sY29sPWMoImdyZXkiLCAicmVkIilbdmFyY2F0XSkKYXhpcyhzaWRlPTEsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJkYXJrZ3JheSIpCmF4aXMoc2lkZT0zLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iZGFya2dyYXkiKQpheGlzKHNpZGU9MiwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImRhcmtncmF5IikKYXhpcyhzaWRlPTQsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJkYXJrZ3JheSIpCmxlZ2VuZCgiYm90dG9tbGVmdCIsbGV2ZWxzKHZhcmNhdCkscGNoPTEsY29sPWMoImdyZXkiLCAicmVkIiksIGNleD0wLjYpCgoKIyBPdmVycHJvamVjdCBUSEUgQ0RHIE9GICBMRVZFTFMgT0YgdmFyY2F0CmZkaWMxID0gdGFwcGx5KFBzaVssMV0sdmFyY2F0LG1lYW4pCmZkaWMyID0gdGFwcGx5KFBzaVssMl0sdmFyY2F0LG1lYW4pIAoKdGV4dChmZGljMSxmZGljMixsYWJlbHM9bGV2ZWxzKGZhY3Rvcih2YXJjYXQpKSxjb2w9ImN5YW4iLCBjZXg9MC43NSkKCmBgYAoKQWx0aG91Z2ggdGhlcmUgYXJlIG5vdCBtYW55IGV4cGxpY2l0IHNvbmdzLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGV4cGxpY2l0IHNvbmdzIHRlbmQgdG8gYmUgb24gdGhlIGxlZnQgc2lkZSBvZiBQQzEuCgoKYGBge3J9CgojIFBST0pFQ1RJT04gT0YgSUxMVVNUUkFUSVZFIHF1YWxpdGF0aXZlIHZhcmlhYmxlcyBvbiBpbmRpdmlkdWFscycgbWFwCnZhcmNhdD1mYWN0b3IoZGRbLDE4XSkKcGxvdChQc2lbLDFdLFBzaVssMl0sY29sPXJhaW5ib3coOSlbdmFyY2F0XSkKYXhpcyhzaWRlPTEsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJkYXJrZ3JheSIpCmF4aXMoc2lkZT0zLCBwb3M9IDAsIGxhYmVscyA9IEYsIGNvbD0iZGFya2dyYXkiKQpheGlzKHNpZGU9MiwgcG9zPSAwLCBsYWJlbHMgPSBGLCBjb2w9ImRhcmtncmF5IikKYXhpcyhzaWRlPTQsIHBvcz0gMCwgbGFiZWxzID0gRiwgY29sPSJkYXJrZ3JheSIpCmxlZ2VuZCgiYm90dG9tbGVmdCIsbGV2ZWxzKHZhcmNhdCkscGNoPTEsY29sPXJhaW5ib3coOSksIGNleD0wLjYpCgoKIyBPdmVycHJvamVjdCBUSEUgQ0RHIE9GICBMRVZFTFMgT0YgdmFyY2F0CmZkaWMxID0gdGFwcGx5KFBzaVssMV0sdmFyY2F0LG1lYW4pCmZkaWMyID0gdGFwcGx5KFBzaVssMl0sdmFyY2F0LG1lYW4pIAoKdGV4dChmZGljMSxmZGljMixsYWJlbHM9bGV2ZWxzKGZhY3Rvcih2YXJjYXQpKSxjb2w9ImN5YW4iLCBjZXg9MC43NSkKCmBgYAoKRnJvbSB0aGUgdGVtcG8gY2F0ZWdvcmllcyBwbG90IHdlIHNlZSB0aGF0IHRoZSBsZWZ0IHNpZGUgb2YgUENBIGlzIHRoZSBmYXN0ZXIgc29uZ3MsIGFuZCB0aGUgcmlnaHQgc2lkZSBhcmUgdGhlIHNsb3dlciBzb25ncy4KCiMjIENvbmNsdXNpb24gLSBXaGF0IHdlIGxlYXJuZWQKCldpdGggdGhlIFBDQSBtZXRob2QsIHdl4oCZcmUgYWJsZSB0byBzZWUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG51bWVyaWNhbCB2YXJpYWJsZXMgYW5kIHdoaWNoIG9uZXMgYWNjb3VudCBmb3IgdGhlIG1vc3QgdmFyaWFuY2UuCldlIGFyZSBhbHNvIGFibGUgdG8gcGxvdCB0aGUgc29uZ3MgYWxvbmcgdGhlIHByaW5jaXBhbCBjb21wb25lbnQgYXhlcyBhbmQgc2VlIHRoYXQgc29tZSBnZW5yZXMgaGF2ZSBzaW1pbGFyIGNoYXJhY3RlcmlzdGljcy4KTmV4dCwgd2XigJlsbCBjb250aW51ZSBleHBsb3JpbmcgdGhlc2Ugc2ltaWxhcml0eSAoYW5kIGRpc3NpbWlsYXJpdGllcykgaW4gQ2x1c3RlcmluZy4K